{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using Paddle Quantum to connect to a quantum computer via the QuLeaf platform\n",
    "\n",
    "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "In quantum machine learning, the ultimate goal we want to achieve is to use quantum computers and classical computers for hybrid computation, thus enabling efficient Quantum Machine Learning (QML) algorithms. In Paddle Quantum, we also support the implementation of QML algorithms using quantum computers by connecting to a real quantum computer via [QuLeaf](https://quantum-hub.baidu.com/), a cloud-native quantum computing platform.\n",
    "\n",
    "### Introduction to QuLeaf\n",
    "\n",
    "QuLeaf is a cloud-native quantum computing platform launched by the Institute of Quantum Computing of Baidu Research Institute. QuLeaf is a good implementation of the deep integration of quantum computing and cloud computing. QuLeaf includes various usage methods such as local simulator, cloud-based simulator and cloud-based quantum computer. Among them, the use of cloud simulator and cloud quantum computer needs to consume QuLeaf credits, users need to register on the [QuLeaf website](https://quantum-hub.baidu.com/) to get QuLeaf account and points. QuLeaf includes QComposer, PyOnline, YunOnline,  QCompute SDK and so on. Among them, QCompute SDK allows us to make calls through Python. In the Python environment, you need to enter the token corresponding to your account to make the call, and the token can be viewed at https://quantum-hub.baidu.com/token. If you want to use the cloud-based simulator or cloud-based quantum computer, you need to consume QuLeaf credits which can be obtained in the  https://quantum-hub.baidu.com/feedback."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paddle Quantum calls QuLeaf\n",
    "\n",
    "### Environment settings\n",
    "\n",
    "If you want to use paddle-quantum to connect to the real quantum computer via QuLeaf, you need to ensure that the installed version of qcompute is above 3.0.0. You can use `pip install -U qcompute` to install the latest version of qcompute.\n",
    "\n",
    "In particular, qcompute requires protobuf version 4.21.1, while paddlepaddle requires protobuf versions 3.1.0 to 3.20.0. Therefore, you need to set environment variables to make paddlepaddle compatible with higher versions of protobuf.\n",
    "\n",
    "> Because paddlepaddle requires protobuf versions 3.1.0 through 3.20.0, all your python programs need to set environment variables so that they can import the paddlepaddle successfully. Therefore, if you don't need to use the qcompute, you can use `pip install protobuf==3.20.0` to ley you import the paddlepaddle without setting environment variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set the environment variable to make paddlepaddle compatible with the higher version of protobuf\n",
    "import os\n",
    "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, just use `paddle_quantum.set_backend('quleaf')` to set the backend of the quantum paddle to QuLeaf. In addition to that, we need to set the simulation method of the QuLeaf. If we use cloud computation power, we also need to enter the token, so the complete setup code is as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle_quantum\n",
    "from QCompute import BackendName\n",
    "paddle_quantum.set_backend('quleaf')\n",
    "paddle_quantum.backend.quleaf.set_quleaf_backend(BackendName.LocalBaiduSim2)\n",
    "# If you are using the local simulator, you don't need to enter your token\n",
    "# paddle_quantum.backend.quleaf.set_quleaf_token('your token')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `set_quleaf_backend()` function allows you to set the quantum end of QuLeaf.\n",
    "\n",
    "Currently, all the end the QuLeaf are as follows:\n",
    "\n",
    "| Type | Quantum End | Description |\n",
    "| :---: | :---: | :---: |\n",
    "| Local | LocalBaiduSim2 | Sim2, local simulator, Python version |\n",
    "| Cloud | CloudBaiduSim2Water | Sim2, Cloud Simulator, C++ Version, Dockerd Multiple Instances |\n",
    "| Cloud | CloudBaiduSim2Earth | Sim2, Cloud Simulator, Python Version, Single Instance, high-performance |\n",
    "| Cloud | CloudBaiduSim2Thunder | Sim2, Cloud Simulator, C++ Version, Single Instance, high-performance |\n",
    "| Cloud | CloudBaiduSim2Wind | Sim2, Cloud Simulator, C++ Version, Single Instance, Sparse |\n",
    "| Cloud | CloudBaiduSim2Heaven | Sim2, Cloud Simulator, C++ Version, Single Instance, Cluster |\n",
    "| Cloud | CloudBaiduSim2Lake | Sim2, Cloud Simulator, C++ Version, Single Instance, GPU |\n",
    "| Cloud | CloudAerAtBD | Aer, Cloud Simulator, C++ Version (open-source) |\n",
    "| Cloud | CloudIoPCAS | QPU 10-qubit quantum computer, from the Institute of Physics CAS |\n",
    "\n",
    "In this case, `LocalBaiduSim2` is a local simulator, which does not require token input and does not consume the QuLeaf credits; `CloudIoPCAS` is a real quantum computer, both the quantum computer and the simulator in the cloud require token input and consume the QuLeaf credits."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bell state preparation\n",
    "\n",
    "Here, we test whether the paddle quantum is successfully connected to the QuLeaf by the example of the Bell state preparation. After executing the above code, the user has already set the paddle quantum to the QuLeaf mode."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_qubits = 2\n",
    "init_state = paddle_quantum.state.zero_state(num_qubits)\n",
    "circuit = paddle_quantum.ansatz.Circuit(num_qubits)\n",
    "circuit.h(0)\n",
    "circuit.cnot([0, 1])\n",
    "bell_state = circuit(init_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the above code runs successfully, it means that it can connect to the QuLeaf and execute the quantum circuit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VQE Example\n",
    "\n",
    "Here, we use VQE as an example to show how to use the QuLeaf through the paddle quantum. We use QuLeaf's local simulator for the demo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The iter is   0, loss is -0.02081.\n",
      "The iter is  10, loss is -0.54088.\n",
      "The iter is  20, loss is -0.83405.\n",
      "The iter is  30, loss is -1.01126.\n",
      "The iter is  40, loss is -1.10169.\n",
      "The iter is  50, loss is -1.10362.\n",
      "The iter is  60, loss is -1.11572.\n",
      "The iter is  70, loss is -1.11396.\n",
      "The theoretical value is -1.137283834485513.\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "\n",
    "# define the hamiltonian\n",
    "hamiltonian_list = [\n",
    "    [-0.0970662686176252, 'I'],\n",
    "    [-0.04530261550868938, 'X0, X1, Y2, Y3'],\n",
    "    [0.04530261550868938, 'X0, Y1, Y2, X3'],\n",
    "    [0.04530261550868938, 'Y0, X1, X2, Y3'],\n",
    "    [-0.04530261550868938, 'Y0, Y1, X2, X3'],\n",
    "    [0.1714128263940238, 'Z0'],\n",
    "    [0.16868898168693292, 'Z0, Z1'],\n",
    "    [0.12062523481381847, 'Z0, Z2'],\n",
    "    [0.1659278503225078, 'Z0, Z3'],\n",
    "    [0.17141282639402383, 'Z1'],\n",
    "    [0.1659278503225078, 'Z1, Z2'],\n",
    "    [0.12062523481381847, 'Z1, Z3'],\n",
    "    [-0.22343153674664024, 'Z2'],\n",
    "    [0.17441287610651632, 'Z2, Z3'],\n",
    "    [-0.2234315367466403, 'Z3'],\n",
    "]\n",
    "\n",
    "# define the quantum circuit\n",
    "num_qubits = 4\n",
    "circuit = paddle_quantum.ansatz.Circuit(num_qubits)\n",
    "circuit.ry('full')\n",
    "circuit.cnot('cycle')\n",
    "circuit.ry('full')\n",
    "circuit.cnot('cycle')\n",
    "circuit.ry('full')\n",
    "# print(circuit)\n",
    "\n",
    "# define the initial quantum state and the optimizer\n",
    "init_state = paddle_quantum.state.zero_state(num_qubits)\n",
    "optimizer = paddle.optimizer.Adam(learning_rate=0.1, parameters=circuit.parameters())\n",
    "hamiltonian = paddle_quantum.Hamiltonian(hamiltonian_list)\n",
    "loss_func = paddle_quantum.loss.ExpecVal(hamiltonian, shots=10000)\n",
    "# iterative training\n",
    "num_itr = 80\n",
    "for itr in range(num_itr):\n",
    "    state = circuit(init_state)\n",
    "    loss = loss_func(state)\n",
    "    loss.backward()\n",
    "    optimizer.minimize(loss)\n",
    "    optimizer.clear_grad()\n",
    "    if itr % 10 == 0:\n",
    "        print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n",
    "print(\"The theoretical value is -1.137283834485513.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "From the above training process, we can see that we can implement quantum machine learning algorithm by paddle quantum calling the QuLeaf.\n",
    "\n",
    "Finally, let's summarize the usage of the paddle quantum to call the QuLeaf. All you need to do is to set the backend of the paddle quantum and the QuLeaf in the beginning of the program. Note that many functions are not supported on the QuLeaf, only quantum circuit related functions are supported."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.0 ('temp')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.0"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "73730e2524c172926674de584a45f4a289689f765fd1f4813f545a2476542e53"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
